<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>data process</title>
    <style>
      canvas {
        background: rgb(0, 0, 0);
        background-image: url('./assets/data/background-small.png');
        /* background-repeat: no-repeat; */
        background-size: 600px 600px;
      }
      .g6-tooltip {
        border: 1px solid #e2e2e2;
        border-radius: 4px;
        font-size: 12px;
        color: #545454;
        background-color: rgba(255, 255, 255, 0.9);
        padding: 10px 8px;
        box-shadow: rgb(174, 174, 174) 0px 0px 10px;
      }
    </style>
  </head>
  <body>
    <div id="mountNode"></div>
    <script src="../build/g6.js"></script>
    <script src="./assets/jquery-3.2.1.min.js"></script>
    <script>
      const colors = [
        'rgb(64, 174, 247)',
        'rgb(108, 207, 169)',
        'rgb(157, 223, 125)',
        'rgb(240, 198, 74)',
        'rgb(221, 158, 97)',
        'rgb(141, 163, 112)',
        'rgb(115, 136, 220)',
        'rgb(133, 88, 219)',
        'rgb(203, 135, 226)',
        'rgb(227, 137, 163)',
      ];
      // custom the node
      G6.registerNode(
        'breath-node',
        {
          afterDraw(cfg, group) {
            const r = cfg.size / 2;
            console.log(cfg);
            const back1 = group.addShape('circle', {
              zIndex: -3,
              attrs: {
                x: 0,
                y: 0,
                r,
                fill: cfg.color || (cfg.style && cfg.style.fill),
                opacity: 0.6,
              },
            });
            const back2 = group.addShape('circle', {
              zIndex: -2,
              attrs: {
                x: 0,
                y: 0,
                r,
                fill: cfg.color,
                // 为了显示清晰，随意设置了颜色
                opacity: 0.6,
              },
            });
            const back3 = group.addShape('circle', {
              zIndex: -1,
              attrs: {
                x: 0,
                y: 0,
                r,
                fill: cfg.color,
                opacity: 0.6,
              },
            });
            group.sort(); // 排序，根据zIndex 排序
            const delayBase = Math.random() * 2000;
            back1.animate(
              {
                // 逐渐放大，并消失
                r: r + 10,
                opacity: 0.0,
              },
              {
                repeat: true, // 循环
                duration: 3000,
                easing: 'easeCubic',
                delay: delayBase, // 无延迟
              },
            );
            back2.animate(
              {
                // 逐渐放大，并消失
                r: r + 10,
                opacity: 0.0,
              },
              {
                repeat: true, // 循环
                duration: 3000,
                easing: 'easeCubic',
                delay: delayBase + 1000, // 1 秒延迟
              },
            );
            back3.animate(
              {
                // 逐渐放大，并消失
                r: r + 10,
                opacity: 0.0,
              },
              {
                repeat: true, // 循环
                duration: 3000,
                easing: 'easeCubic',
                delay: delayBase + 2000, // 2 秒延迟
              },
            );
          },
        },
        'circle',
      );

      // custom the edge
      G6.registerEdge(
        'running-polyline',
        {
          afterDraw(cfg, group) {
            const shape = group.get('children')[0];
            const length = shape.getTotalLength();
            const startPoint = shape.getPoint(0);
            let circleCount = Math.ceil(length / 20);
            circleCount = circleCount === 0 ? 1 : circleCount;
            for (let i = 0; i < circleCount; i++) {
              const delay = Math.random() * 1000;
              const start = shape.getPoint(i / circleCount);
              const circle = group.addShape('circle', {
                attrs: {
                  x: start.x,
                  y: start.y,
                  r: 0.8,
                  fill: '#A0F3AF',
                  shadowColor: '#fff',
                  shadowBlur: 30,
                },
              });
              circle.animate(
                ratio => {
                  ratio += i / circleCount;
                  if (ratio > 1) {
                    ratio %= 1;
                  }
                  const tmpPoint = shape.getPoint(ratio);
                  return {
                    x: tmpPoint.x,
                    y: tmpPoint.y,
                  };
                },
                {
                  repeat: true,
                  duration: 10 * length,
                  easing: 'easeCubic',
                  delay,
                },
              );
            }
          },
        },
        'polyline',
      );

      const size = window.innerHeight;
      const graphSize = [size, size];
      const graph = new G6.Graph({
        container: 'mountNode',
        width: graphSize[0],
        height: graphSize[1],
        modes: {
          default: [
            {
              type: 'edge-tooltip',
              formatText(model) {
                const text = model.class;
                return text;
              },
              shouldUpdate: e => {
                return true;
              },
            },
          ],
        },
        defaultNode: {
          shape: 'breath-node',
          size: 3,
          style: {
            lineWidth: 0,
            fill: 'rgb(240, 223, 83)',
          },
        },
        defaultEdge: {
          shape: 'running-polyline',
          size: 1,
          color: 'rgb(14,142,63)',
          style: {
            opacity: 0.4,
            lineAppendWidth: 3,
          },
        },
      });
      $.getJSON(
        'https://gw.alipayobjects.com/os/basement_prod/8c2353b0-99a9-4a93-a5e1-3e7df1eac64f.json',
        data => {
          const nodes = data.nodes;
          const edges = data.edges;
          const classMap = new Map();
          let classId = 0;
          nodes.forEach(node => {
            node.y = -node.y;
          });
          edges.forEach(edge => {
            // edge cluster
            if (edge.class && classMap.get(edge.class) === undefined) {
              classMap.set(edge.class, classId);
              classId++;
            }
            const cid = classMap.get(edge.class);
            edge.color = colors[cid % colors.length];
            const controlPoints = edge.controlPoints;

            controlPoints.forEach(cp => {
              cp.y = -cp.y;
            });
          });
          scaleNodesPoints(nodes, edges, graphSize);
          graph.data(data);
          graph.render();
        },
      );

      function scaleNodesPoints(nodes, edges, graphSize) {
        const size = graphSize[0] < graphSize[1] ? graphSize[0] : graphSize[1];
        let minX = 99999999999999999;
        let maxX = -99999999999999999;
        let minY = 99999999999999999;
        let maxY = -99999999999999999;
        nodes.forEach(node => {
          if (node.x > maxX) maxX = node.x;
          if (node.x < minX) minX = node.x;
          if (node.y > maxY) maxY = node.y;
          if (node.y < minY) minY = node.y;
        });

        edges.forEach(edge => {
          const controlPoints = edge.controlPoints;
          controlPoints.forEach(cp => {
            if (cp.x > maxX) maxX = cp.x;
            if (cp.x < minX) minX = cp.x;
            if (cp.y > maxY) maxY = cp.y;
            if (cp.y < minY) minY = cp.y;
          });
        });

        const xScale = maxX - minX;
        const yScale = maxY - minY;
        nodes.forEach(node => {
          node.orix = node.x;
          node.oriy = node.y;
          node.x = ((node.x - minX) / xScale) * size;
          node.y = ((node.y - minY) / yScale) * size;
        });
        edges.forEach(edge => {
          const controlPoints = edge.controlPoints;
          controlPoints.forEach(cp => {
            cp.x = ((cp.x - minX) / xScale) * size;
            cp.y = ((cp.y - minY) / yScale) * size;
          });
        });
      }
    </script>
  </body>
</html>
